// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#if defined(__ANDROID__)
#undef __ANDROID__
#endif

#include "catch.hpp"

#include "rotor-light/planner.hpp"
#include "rotor-light/queue.hpp"
#include "rotor-light/supervisor.hpp"

using namespace rotor_light;

struct Sample : Message {
  static constexpr auto type_id = __LINE__;
  using Message::Message;
};

struct LogMessage : Message {
  static constexpr auto type_id = __LINE__;
  LogMessage(ActorId to, const char *msg_) : Message(to), msg{msg_} {}
  const char *msg;
};

using MessageStorage =
    traits::MessageStorage<message::ChangeState, message::ChangeStateAck,
                           Sample, LogMessage>;

using AppPlanner = Planner<1>;

static int overfill = 0;

namespace rotor_light {

void on_queue_full() { ++overfill; }

} // namespace rotor_light

TEST_CASE("simple ping-pong example", "[actor]") {
  using AppQueue = Queue<MessageStorage, 5>;
  using AppSupervisor = Supervisor<3>;
  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, nullptr};
  AppSupervisor sup;
  sup.bind(context);

  sup.start();
  sup.process();
  CHECK(overfill == 0);

  for (int i = 0; i < 5; ++i) {
    bool ok = sup.send<ctx::thread, Sample>(0, sup.get_id());
    CHECK(ok);
  }
  CHECK(overfill == 0);

  bool ok = sup.send<ctx::thread, Sample>(0, sup.get_id());
  CHECK(!ok);
  CHECK(overfill == 0);
  sup.stop();
  CHECK(overfill == 1);
  sup.process();

  sup.stop();
  sup.process();

  CHECK(sup.get_state() == State::off);
}

TEST_CASE("transmit log message") {
  struct A : Actor<2> {
    using Parent = Actor<2>;
    using Parent::Parent;

    void initialize() override {
      subscribe(&A::on_message);
      Parent::initialize();
    }

    void on_message(LogMessage &msg) { msgs[idx++] = msg.msg; }

    const char *msgs[2];
    int idx = 0;
  };

  using AppQueue = Queue<MessageStorage, 5, 2>;
  using AppSupervisor = Supervisor<3, A>;

  AppQueue queue;
  AppPlanner planner;
  Context context{&queue, &planner, nullptr};
  AppSupervisor sup;
  sup.bind(context);

  sup.start();
  sup.process();

  auto a = sup.get_child<0>();
  CHECK(!a->transmit<ctx::thread, LogMessage>(1, a->get_id(), "first"));
  CHECK(!a->transmit<ctx::thread, LogMessage>(1, a->get_id(), "second"));
  CHECK(a->transmit<ctx::thread, LogMessage>(1, a->get_id(), "third"));
  sup.process();

  CHECK(a->idx == 2);
  CHECK(a->msgs[0] == std::string_view("second"));
  CHECK(a->msgs[1] == std::string_view("third"));

  sup.stop();
  sup.process();
  CHECK(sup.get_state() == State::off);
}
